package org.example.webservice;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.google.common.collect.Lists;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;
import org.example.webservice.agency.AgencyService;
import org.example.webservice.agency.IAgencyService;
import org.example.webservice.interceptor.WsUserOutInterceptor;
import org.example.webservice.user.IUserService;
import org.example.webservice.user.User;
import org.example.webservice.user.UserService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import java.net.URL;
import java.util.List;
import java.util.Map;

/**
 * WebService 客户端
 *
 * @author wangMaoXiong
 * @version 1.0
 * @date 2023/10/15 16:26
 */
@RestController
@SuppressWarnings("Duplicates")
public class WebServiceClient {

    /**
     * 原生 Java JDK 服务工厂方式调用。
     * 1、客户端不用导入任何与webservice相关的依赖包(如apache的cxf)，也和 Spring boot 无关。
     * 2、http://localhost:8081/wsClient/queryAgencyStatInfo1
     *
     * @param bodyMap : {"user_id":"15424JK411","token":"741541212","user_name":"蚩尤后裔"}
     * @return
     * @throws Exception
     */
    @PostMapping("/wsClient/queryAgencyStatInfo1")
    public Map<String, Object> queryAgencyStatInfo1(@RequestBody Map<String, Object> bodyMap) throws Exception {
        /**
         * url：webservice 服务端提供的服务地址，结尾必须加 ?wsdl
         * 1、可以直接访问在线地址，也可以通过浏览器访问后，然后将内容复制粘贴保存到本地项目中。
         */
        // URL url = new URL("http://localhost:8080/webservice/agencyService?wsdl");
        URL url = WebServiceClient.class.getClassLoader().getResource("wsdl/agencyService.wsdl");
        /**
         * QName 表示 XML 规范中定义的限定名称，QName 的值包含名称空间 URI、本地部分和前缀。
         * QName(final String namespaceURI, final String localPart)：指定名称空间 URI 和本地部分的 QName 构造方法。
         * 1、两个参数值都可以在 wsdl 文件中的第一行中找到，如下所示，就是其中的两个属性。
         * * * <wsdl:definitions name="agencyService" targetNamespace="http://webService.web_app.wmx.com/">
         * 2、或者在生成的接口的实现类中也能找到，如下所示，同样是 name 和 targetNamespace 属性。
         * @WebServiceClient(name = "agencyService", targetNamespace = "http://webService.web_app.wmx.com/", wsdlLocation = "http://localhost:8080/webservice/agencyService?wsdl")
         */
        QName qName = new QName("http://webService.web_app.wmx.com/", "agencyService");
        /**
         * Service 对象提供 Web 服务的客户端视图
         * Service 作为以下内容的工厂：
         * * 1、目标服务端点的代理，
         * * 2、用于远程操作的动态面向消息调用的 javax.xml.ws.Dispatch 实例。
         * create(java.net.URL wsdlDocumentLocation,QName serviceName):创建 Service 实例。
         * wsdlDocumentLocation ： 服务 WSDL 文档位置的 URL
         * serviceName ： 服务的 QName
         */
        Service service = Service.create(url, qName);
        /**
         * 使用 getPorts 方法可以对服务上可用的端口/接口进行枚举
         * getPort(Class<T> serviceEndpointInterface)：获取支持指定服务端点接口的对象实例
         */
        IAgencyService agencyService = service.getPort(IAgencyService.class);

        /**
         * Java JDK 原生的 JWS 是没有拦截器的。以下代码为 Apache CXF 代码。
         * 添加拦截器代码如同服务端一样，都是先获取拦截器列表，然后添加需要使用的拦截器
         * org.apache.cxf.frontend.ClientProxy ：apache cxf 客户端代理
         * Client getClient(Object o) 返回发送请求的客户端对象，参数传入服务端接口对象
         * 服务端是先进再出，客户端是先出再进。
         */
        Client client = ClientProxy.getClient(agencyService);
        client.getOutInterceptors().add(new LoggingOutInterceptor());
        client.getInInterceptors().add(new LoggingInInterceptor());

        String bodyParams = JSONUtil.toJsonStr(bodyMap);
        String agencyStatInfo = agencyService.queryAgencyStatInfo(bodyParams);

        return JSONUtil.toBean(agencyStatInfo, Map.class);
    }

    /**
     * 原生 Java JDK 面向接口方式调用。
     * 注意事项：如果远程接口返回的是Map数据类型，则JDK原生的JWS是不支持的，但是CXF支持，此时服务端、客户端都需要使用cxg，且使用cxf的工具生成客户端代码。
     * 1、客户端不用导入任何与webservice相关的依赖包(如apache的cxf)，也和 Spring boot 无关。
     * 2、将工具生成的客户端代码全部引入，直接调用本地接口与实现类来调用远程服务接口。连地址都可以省略不写。
     * 3、http://localhost:8081/wsClient/queryAgencyStatInfo2
     *
     * @param bodyMap : {"user_id":"254578451PL","token":"6451215","user_name":"蚩尤后裔"}
     * @return
     * @throws Exception
     */
    @PostMapping("/wsClient/queryAgencyStatInfo2")
    public Map<String, Object> queryAgencyStatInfo2(@RequestBody Map<String, Object> bodyMap) {
        // 直接创建接口实现类的对象
        AgencyService agencyService = new AgencyService();
        // 然后调用getXxxPort方法得到目标接口
        IAgencyService agencyServicePort = agencyService.getAgencyServicePort();

        /**
         * Java JDK 原生的 JWS 是没有拦截器的。以下代码为 Apache CXF 代码。
         * 添加拦截器代码如同服务端一样，都是先获取拦截器列表，然后添加需要使用的拦截器
         * org.apache.cxf.frontend.ClientProxy ：apache cxf 客户端代理
         * Client getClient(Object o) 返回发送请求的客户端对象，参数传入服务端接口对象
         * 服务端是先进再出，客户端是先出再进。
         */
        Client client = ClientProxy.getClient(agencyServicePort);
        client.getOutInterceptors().add(new LoggingOutInterceptor());
        client.getInInterceptors().add(new LoggingInInterceptor());

        // 最后直接调用接口方法
        String agencyStatInfo = agencyServicePort.queryAgencyStatInfo(JSONUtil.toJsonStr(bodyMap));
        return JSONUtil.toBean(agencyStatInfo, Map.class);
    }

    /**
     * 原生 Java JDK 面向接口方式调用。
     * http://localhost:8081/wsClient/listUserByIds
     *
     * @return
     * @throws Exception
     */
    @GetMapping("/wsClient/listUserByIds")
    public List<User> listUserByIds() {
        // 直接创建接口实现类的对象
        UserService userService = new UserService();
        // 然后调用getXxxPort方法得到目标接口
        IUserService iUserService = userService.getUserServicePort();

        // 配置拦截器
        Client client = ClientProxy.getClient(iUserService);
        client.getOutInterceptors().add(new LoggingOutInterceptor());
        client.getOutInterceptors().add(new WsUserOutInterceptor("admin", "123456"));

        List<User> users = iUserService.listUserByIds(Lists.newArrayList(1L, 2L, 3L, 4L, 5L));
        return users;
    }

    /**
     * Apache cxf 动态工厂方式 —— 查询单位信息
     * 需要引用 Apache cxf 依赖。
     * http://localhost:8081/wsClient/queryAgencyStatInfo3
     *
     * @param bodyMap : {"user_id":"4783748PL898878","token":"37483748YU23","user_name":"蚩尤后裔"}
     * @return
     * @throws Exception
     */
    @PostMapping("/wsClient/queryAgencyStatInfo3")
    public Map<String, Object> queryAgencyStatInfo(@RequestBody Map<String, Object> bodyMap) throws Exception {
        /**
         * 服务端 wsdl 地址
         * 1、可以直接访问在线地址，也可以通过浏览器访问后，然后将内容复制粘贴保存到本地项目中。
         */
        // String wsdlUrl = "http://localhost:8080/webservice/agencyService?wsdl";
        URL wsdlUrl = WebServiceClient.class.getClassLoader().getResource("wsdl/agencyService.wsdl");

        // 代理工厂
        JaxWsDynamicClientFactory wsDynamicClientFactory = JaxWsDynamicClientFactory.newInstance();
        // 动态客户端
        Client client = wsDynamicClientFactory.createClient(wsdlUrl);

        /**
         * 添加拦截器代码如同服务端一样，都是先获取拦截器列表，然后添加需要使用的拦截器
         * org.apache.cxf.frontend.ClientProxy ：apache cxf 客户端代理
         * Client getClient(Object o) 返回发送请求的客户端对象，参数传入服务端接口对象
         * 服务端是先进再出，客户端是先出再进。
         */
        client.getOutInterceptors().add(new LoggingOutInterceptor());
        client.getInInterceptors().add(new LoggingInInterceptor());

        String bodyParams = JSONUtil.toJsonStr(bodyMap);
        // 调用指定的方法 invoke("方法名",参数1,参数2,参数3....);
        Object[] invoke = client.invoke("queryAgencyStatInfo", bodyParams);
        // 获取返回值
        JSONObject jsonObject = JSONUtil.parseObj(invoke[0]);
        Map map = JSONUtil.toBean(jsonObject, Map.class);
        return map;
    }

    /**
     * Apache cxf 代理方式
     * 需要引用 Apache cxf 依赖。
     * http://localhost:8081/wsClient/syncAgencyStatInfo1
     *
     * @param bodyMap : {"user_id":"5414512HY745","token":"T45124512","user_name":"蚩尤后裔"}
     * @return
     * @throws Exception
     */
    @PostMapping("/wsClient/syncAgencyStatInfo1")
    public Map<String, Object> syncAgencyStatInfo1(@RequestBody Map<String, Object> bodyMap) throws Exception {
        // 服务端 wsdl 地址
        String wsdlUrl = "http://localhost:8080/webservice/agencyService?wsdl";
        // 代理工厂
        JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
        // 设置代理地址
        jaxWsProxyFactoryBean.setAddress(wsdlUrl);
        // 指定代理实现的SEI（service endpoint interface）的类
        jaxWsProxyFactoryBean.setServiceClass(IAgencyService.class);

        // 配置拦截器，与使用 Client client = ClientProxy.getClient(xxx) 设置拦截器是一样的。
        // jaxWsProxyFactoryBean.getOutInterceptors().add(new LoggingOutInterceptor());

        // 创建一个可用于进行远程调用的JAX-WS代理。必须将返回的对象强制转换为适当的类
        IAgencyService agencyService = (IAgencyService) jaxWsProxyFactoryBean.create();

        /**
         * 添加拦截器代码如同服务端一样，都是先获取拦截器列表，然后添加需要使用的拦截器
         * org.apache.cxf.frontend.ClientProxy ：apache cxf 客户端代理
         * Client getClient(Object o) 返回发送请求的客户端对象，参数传入服务端接口对象
         * 服务端是先进再出，客户端是先出再进。
         */
        Client client = ClientProxy.getClient(agencyService);
        client.getOutInterceptors().add(new LoggingOutInterceptor());
        client.getInInterceptors().add(new LoggingInInterceptor());

        String agencyStatInfo = agencyService.syncAgencyStatInfo(JSONUtil.toJsonStr(bodyMap));
        return JSONUtil.toBean(agencyStatInfo, Map.class);
    }

    /**
     * Apache cxf 代理方式
     * 需要引用 Apache cxf 依赖。
     * http://localhost:8081/wsClient/listUserByIds2
     *
     * @return
     * @throws Exception
     */
    @GetMapping("/wsClient/listUserByIds2")
    public List<User> listUserByIds2() {
        // 服务端 wsdl 地址
        String wsdlUrl = "http://localhost:8080/webservice/userService?wsdl";
        // 代理工厂
        JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
        // 设置代理地址
        jaxWsProxyFactoryBean.setAddress(wsdlUrl);
        // 指定代理实现的SEI（service endpoint interface）的类
        jaxWsProxyFactoryBean.setServiceClass(IUserService.class);

        // 配置拦截器，与使用 Client client = ClientProxy.getClient(xxx) 设置拦截器是一样的。
        jaxWsProxyFactoryBean.getOutInterceptors().add(new LoggingOutInterceptor());
        jaxWsProxyFactoryBean.getOutInterceptors().add(new WsUserOutInterceptor("admin", "123456"));

        // 创建一个可用于进行远程调用的JAX-WS代理。必须将返回的对象强制转换为适当的类
        IUserService iUserService = (IUserService) jaxWsProxyFactoryBean.create();

        List<User> users = iUserService.listUserByIds(Lists.newArrayList(1L, 2L, 3L, 4L, 5L));
        return users;
    }

    /**
     * 明文
     *
     * @param args
     */
   /* public static void main(String[] args) {
        try {
            // 接口地址
            String address = "http://localhost:9000/webservice/queryAgencysInfo?wsdl";
            // 代理工厂
            JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
            // 设置代理地址
            jaxWsProxyFactoryBean.setAddress(address);
            // 设置接口类型
            jaxWsProxyFactoryBean.setServiceClass(IBasicWebServicesService.class);
            jaxWsProxyFactoryBean.getOutInterceptors().add(new ClientLoginInterceptor("root", "admin"));

            // 创建一个代理接口实现
            IBasicWebServicesService userInfoWebService = (IBasicWebServicesService) jaxWsProxyFactoryBean.create();
            // 调用代理接口的方法调用并返回结果
            List<AgencyInfo> userInfo = userInfoWebService.getAgencysInfo("430000000", "2023");
            userInfo.forEach(agencyInfo -> {
                System.out.println(agencyInfo.getAgencyCode() + ":" + agencyInfo.getAgencyName());
            });
            String agencyInfo = userInfoWebService.getAgencysXML("621121000", "2023");
            System.out.println(agencyInfo);
            List<String> array = Arrays.asList(new String[]{"head", "datasets", "dataset"});
            Map<String, Object> mmp = MapToXMLToMap.parserXml(agencyInfo, array);
            System.out.println(mmp);
            System.out.println("###################人员######################");
            List<PersonInfo> personInfos = userInfoWebService.getPersonsInfo("001001", "430000000", "2023");
            personInfos.forEach(agencyInfo2 -> {
                System.out.println(agencyInfo2.getAgencyCode() + ":" + agencyInfo2.getAgencyName());
            });
            String personXML = userInfoWebService.getAgencysXML( "621121000", "2023");
            System.out.println(personXML);
            Map<String, Object> mmp2 = MapToXMLToMap.parserXml(personXML, array);
            System.out.println(mmp2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    */

    /**
     * 密文
     */
   /* public static void main3(String[] args) {
        try {
            Map<String,Object> outProps = new HashMap<String,Object>();
            outProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
            outProps.put(WSHandlerConstants.USER, "kevin");//这个用户即服务器端配置的用户名
//            outProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_DIGEST);
            outProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
            outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS, ClientPasswordHandler.class.getName());
            ArrayList list = new ArrayList();
            // 添加cxf安全验证拦截器，必须
            list.add(new SAAJOutInterceptor());
            list.add(new WSS4JOutInterceptor(outProps));
            // 接口地址
            String address = "http://localhost:9000/webservice/queryAgencysInfo?wsdl";
            // 代理工厂
            JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
            // 设置代理地址
            jaxWsProxyFactoryBean.setAddress(address);
            // 设置接口类型
            jaxWsProxyFactoryBean.setServiceClass(IBasicWebServicesService.class);
            jaxWsProxyFactoryBean.getOutInterceptors().addAll(list);;

            // 创建一个代理接口实现
            IBasicWebServicesService userInfoWebService = (IBasicWebServicesService) jaxWsProxyFactoryBean.create();
            // 调用代理接口的方法调用并返回结果
            List<AgencyInfo> userInfo = userInfoWebService.getAgencysInfo("430000000", "2023");
            userInfo.forEach(agencyInfo -> {
                System.out.println(agencyInfo.getAgencyCode() + ":" + agencyInfo.getAgencyName());
            });
            String agencyInfo = userInfoWebService.getAgencysXML("test_a", "sadsaddd", "430000000", "2023");
            System.out.println(agencyInfo);
            List<String> array= Arrays.asList(new String[]{"head","datasets","dataset"});
            Map<String, Object>  mmp=MapToXMLToMap.parserXml(agencyInfo,array);
            System.out.println(mmp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }*/


}
